package org.jmpo;

import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

public class SevenDivisibilityRuleTest {
    @Test
    public void someNumberTest() {
        final String input = "10110010";
        final int expect = 3;
        final int actual = new SevenDivisibilityRule(input).process();
        assertEquals(expect, actual);
    }

    @Test
    public void emptyStringTest() {
        final String input = "";
        var thrown = assertThrows(
            IllegalArgumentException.class,
            () -> { new SevenDivisibilityRule(input).process(); });
        assertEquals("An empty sequence", thrown.getMessage());
    }

    @Test
    public void wrongSymbolTest() {
        final String input = "101210";
        assertThrows(
            IllegalArgumentException.class,
            () -> { new SevenDivisibilityRule(input).process(); });
    }

    @Test
    public void veryLongNumberTest() {
        final String input = "10001010010101110101110011110001010111010111010101010100101011111";
        final int expect = 1;
        int actual = new SevenDivisibilityRule(input).process();
        assertEquals(expect, actual);
    }

    @Test
    public void twiceProcessTest() {
        final String input = "10001010010101110101110011110001010111010111010101010100101011111";
        final int expect = 1;
        var processor = new SevenDivisibilityRule(input);
        assertEquals(expect, processor.process());
        assertEquals(expect, processor.process());
    }

    @RepeatedTest(3)
    public void repeatedTest() {
        final String input = "10110010";
        final int expect = 3;
        final int actual = new SevenDivisibilityRule(input).process();
        assertEquals(expect, actual);
    }

    @ParameterizedTest
    @ValueSource(strings = {"0", "1", "10", "11", "100", "101", "110", "111"})
    public void smallNumbersTest(String input) {
        int actual = new SevenDivisibilityRule(input).process();
        switch(input) {
            case "0"   -> assertEquals( 0, actual);
            case "1"   -> assertEquals( 1, actual);
            case "10"  -> assertEquals( 2, actual);
            case "11"  -> assertEquals( 3, actual);
            case "100" -> assertEquals( 4, actual);
            case "101" -> assertEquals( 5, actual);
            case "110" -> assertEquals( 6, actual);
            case "111" -> assertEquals( 0, actual);
        }
    }

    @ParameterizedTest
    @MethodSource ("knownNumbersProviderMethod")
    public void knownNumbersTest(String input, int expected) {
        int actual = new SevenDivisibilityRule(input).process();
        assertEquals(expected, actual);
    }

    private static Stream<Arguments> knownNumbersProviderMethod() {
        return Stream.of(
            Arguments.of("10001000010110011", 0),
            Arguments.of("10001000010110100", 1),
            Arguments.of("10001000010110101", 2),
            Arguments.of("10001000010110110", 3),
            Arguments.of("10001000010110111", 4),
            Arguments.of("10001000010111000", 5),
            Arguments.of("10001000010111001", 6),
            Arguments.of("10001000010111010", 0)
        );
    }
}
